<template>
    <div class="topology-wrapper">
        <div><el-button :icon="Guide" circle @click="$emit('changeMode')" /></div>
        <div class="topology-box">
            <!-- 左侧工具栏 - 用于拖拽添加节点 -->
            <div class="toolbar-box">
                <el-input
                    v-model="filterTxt"
                    placeholder="请输入"
                    :suffix-icon="Search"
                    clearable
                    @input="filterChange"
                />
                <!-- 离散节点 -->
                <ul>
                    <li
                        v-for="item in showDiscreteNode"
                        :key="item.id"
                        class="tool-item"
                        draggable="true"
                        :title="item.displayName && item.displayName.length > 5 ? item.displayName : undefined"
                        @dragstart="handleDragStart($event, item)"
                    >
                        {{ item.displayName }}
                    </li>
                </ul>
            </div>
            <!-- 主画布区域 -->
            <div
                ref="containerBoxRef"
                class="container-box"
                @dragover.prevent="handleDragOver"
                @drop="handleDrop"
                @dragleave="handleDragLeave"
            ></div>
        </div>
    </div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue';
import { Edge, Graph, Node, Shape } from '@antv/x6';
import dagre from 'dagre';
import { Search } from '@element-plus/icons-vue';
import { Guide } from '@element-plus/icons-vue';
import { modelThingService } from '@great/services';

const $emit = defineEmits<{
    (e: 'changeMode'): void;
}>();
const modelDetail = inject<any>('modelDetail')?.value;

interface NodeData {
    id: number;
    displayName: string;
}

const filterTxt = ref('');
const filterChange = () => {
    if (filterTxt.value) {
        showDiscreteNode.value = allDiscreteNode.value.filter(item => item.displayName.includes(filterTxt.value));
    } else {
        showDiscreteNode.value = allDiscreteNode.value;
    }
};

// 布局方向
const dir = ref('LR'); // LR RL TB BT
// 图形实例
const graph = ref<Graph>();
// 定义容器引用
const containerBoxRef = ref<HTMLDivElement>();
// 离散节点数据
const allDiscreteNode = ref<Array<NodeData>>([]);
const showDiscreteNode = ref<Array<NodeData>>([]);
const rootNode = ref<TreeNode>();
// 当前悬停的节点
const hoveredNode = ref<any>(null);
// 原始样式缓存
const originalStyle = ref<any>(null);
// 定义节点
class TreeNode extends Node {
    private collapsed: boolean = false;

    protected postprocess() {
        this.toggleCollapse(false);
    }

    isCollapsed() {
        return this.collapsed;
    }
    removeDelBtn() {
        this.removeAttrByPath('delBtnGroup');
        this.removeAttrByPath('delBtn');
        this.removeAttrByPath('delBtnSymbol');
    }
    operBtnVisibility(visible: boolean) {
        this.attr('delBtnGroup', {
            display: visible ? 'block' : 'none'
        });
    }
    collapseBtnVisibility(visible: boolean) {
        this.attr('collapseBtnGroup', {
            display: visible ? 'block' : 'none'
        });
    }

    toggleCollapse(collapsed?: boolean) {
        const target = collapsed == null ? !this.collapsed : collapsed;
        if (!target) {
            this.attr('collapseBtnSymbol', {
                d: 'M 1 5 9 5 M 5 1 5 9',
                strokeWidth: 1.6
            });
        } else {
            this.attr('collapseBtnSymbol', {
                d: 'M 2 5 8 5',
                strokeWidth: 1.8
            });
        }
        this.collapsed = target;
    }
}
// 定义连线
class TreeEdge extends Shape.Edge {
    isHidden() {
        const node = this.getTargetNode() as TreeNode;
        return !node || !node.isVisible();
    }
}
// 拖拽-开始
const handleDragStart = (event: DragEvent, item: NodeData) => {
    event.dataTransfer?.setData('graphNodeData', JSON.stringify(item));
};
// 拖拽-悬停
const handleDragOver = (event: DragEvent) => {
    event.preventDefault();

    // 计算鼠标在容器中的位置
    const rect = containerBoxRef.value?.getBoundingClientRect();
    if (!rect || !graph.value) return;
};
// 拖拽-释放
const handleDrop = (event: DragEvent) => {
    event.preventDefault();

    const data = event.dataTransfer?.getData('graphNodeData');
    if (data && rootNode.value) {
        addChildNode(JSON.parse(data), rootNode.value);
        // 添加新节点后更新布局
        updateLayout();
    }
};

// 拖拽-离开
const handleDragLeave = () => {
    // 恢复悬停节点的样式
    if (hoveredNode.value && originalStyle.value) {
        hoveredNode.value.attr('rect-box', originalStyle.value);
        hoveredNode.value = null;
        originalStyle.value = null;
    }
};

/**
 * （自动）更新布局
 */
const updateLayout = () => {
    // 获取当前图中的所有节点和连接线
    const nodes = graph.value?.getNodes();
    const edges = graph.value?.getEdges();

    // 创建一个新的 dagre 图对象
    const g = new dagre.graphlib.Graph();

    // 设置图的基本属性：rankdir: 方向（LR表示从左到右）； nodesep: 节点间水平间距；ranksep: 层级间垂直间距
    g.setGraph({ rankdir: dir.value, nodesep: 16, ranksep: 30 });

    // 设置默认的边标签
    g.setDefaultEdgeLabel(() => ({}));

    // 定义节点尺寸
    const width = 110;
    const height = 43;

    // 将所有节点添加到 dagre 图中
    nodes?.forEach(node => {
        g.setNode(node.id, { width, height });
    });

    // 将所有连接关系添加到 dagre 图中
    edges?.forEach(edge => {
        const source = edge.getSourceCellId();
        const target = edge.getTargetCellId();
        g.setEdge(source, target);
    });

    // 执行 dagre 布局计算
    dagre.layout(g);

    // 根据计算结果更新节点位置
    g.nodes().forEach(id => {
        const node = graph.value?.getCellById(id) as Node;
        if (node) {
            const pos = g.node(id);
            node.position(pos.x, pos.y);
        }
    });

    // 更新连接线的路径
    edges?.forEach(edge => {
        const source = edge.getSourceNode()!;
        const target = edge.getTargetNode()!;
        const sourceBBox = source.getBBox();
        const targetBBox = target.getBBox();

        // 根据布局方向调整连接线的路径点
        if ((dir.value === 'LR' || dir.value === 'RL') && sourceBBox.y !== targetBBox.y) {
            // 处理左右布局且节点不在同一水平线的情况
            const gap =
                dir.value === 'LR'
                    ? targetBBox.x - sourceBBox.x - sourceBBox.width
                    : -sourceBBox.x + targetBBox.x + targetBBox.width;
            const fix = dir.value === 'LR' ? sourceBBox.width : 0;
            const x = sourceBBox.x + fix + gap / 2;
            edge.setVertices([
                { x, y: sourceBBox.center.y },
                { x, y: targetBBox.center.y }
            ]);
        } else if ((dir.value === 'TB' || dir.value === 'BT') && sourceBBox.x !== targetBBox.x) {
            // 处理上下布局且节点不在同一垂直线的情况
            const gap =
                dir.value === 'TB'
                    ? targetBBox.y - sourceBBox.y - sourceBBox.height
                    : -sourceBBox.y + targetBBox.y + targetBBox.height;
            const fix = dir.value === 'TB' ? sourceBBox.height : 0;
            const y = sourceBBox.y + fix + gap / 2;
            edge.setVertices([
                { x: sourceBBox.center.x, y },
                { x: targetBBox.center.x, y }
            ]);
        } else {
            // 其他情况下清空路径点
            edge.setVertices([]);
        }
    });
};

/**
 * 添加节点
 * @param nodeData 节点数据
 * @param x 节点横坐标
 * @param y 节点纵坐标
 * @returns 新节点对象
 */
const addNode = (nodeData: NodeData, x?: number, y?: number): TreeNode | undefined => {
    const node = new TreeNode({
        x: x || 0,
        y: y || 0,
        attrs: {
            'text-box': {
                text: `${nodeData.displayName}`
            }
        },
        data: nodeData
    });
    if (node.removeDelBtn) {
        // 根节点不显示删除按钮
        node.removeDelBtn();
    }
    graph.value?.addNode(node);

    // console.log('添加（父）节点对象：', node);
    return node;
};

/**
 * 添加子节点
 * @param nodeData 子节点数据
 * @param parentNode 父节点
 * @returns 新子节点对象
 */
const addChildNode = (nodeData: NodeData, parentNode: TreeNode): TreeNode | undefined => {
    // 定义节点间距和偏移量
    const horizontalOffset = 150; // 相对于父节点的水平偏移

    // 计算新子节点的位置
    const parentPos = parentNode.position();
    const parentSize = parentNode.size();

    const x = parentPos.x + parentSize.width + horizontalOffset;
    const y = parentPos.y;

    // 创建新的（子）节点
    const childNode = new TreeNode({
        x,
        y,
        attrs: {
            'text-box': {
                text: `${nodeData.displayName}`
            }
        },
        data: nodeData
    });
    graph.value?.addNode(childNode);

    // 创建父子节点之间的连线 - 设置固定的源点位置
    if (childNode) {
        const edge = new TreeEdge({
            shape: 'tree-edge',
            source: {
                cell: parentNode.id,
                anchor: 'right'
            },
            target: {
                cell: childNode.id,
                anchor: 'left'
            }
        });
        graph.value?.addEdge(edge);
    }
    if (parentNode.collapseBtnVisibility) {
        // 显示父节点展/收按钮
        parentNode.collapseBtnVisibility(true);
    }
    // console.log('添加（子）节点对象', childNode);
    return childNode;
};
/**
 * 初始化-绘制节点
 * @param originalData （未处理）原始（业务）数据
 * @param parentNode 父节点
 */
const initDrawNode = (originalData: any, parentNode?: TreeNode) => {
    if (parentNode) {
        const nodeObj = addChildNode(originalData, parentNode);
        if (originalData && originalData.components && nodeObj) {
            originalData.components.forEach((childNodeData: any) => {
                initDrawNode(childNodeData, nodeObj);
            });
        }
    } else {
        // 添加父节点
        originalData.forEach((nodeData: any) => {
            const nodeObj = addNode(nodeData);
            rootNode.value = nodeObj; // 根节点
            if (nodeObj) {
                // 添加子节点
                nodeData.components.forEach((childNodeData: any) => {
                    initDrawNode(childNodeData, nodeObj);
                });
            }
        });
    }
};
// 获取节点（业务）数据
const getNodeData = () => {
    modelThingService
        .getComponentList({
            objectName: modelDetail.objectName
        })
        .then(res => {
            initDrawNode(res.data);
            updateLayout();
        });
};
// 获取-离散（可添加）数据
const getDiscreteData = () => {
    modelThingService
        .getAddComponents({
            current: 1,
            size: 9999999,
            currentObject: modelDetail.objectName
        })
        .then(res => {
            allDiscreteNode.value = res.data.records;
            showDiscreteNode.value = res.data.records;
        });
};
// 初始化-节点部件（节点、连线）
const initNodeUnit = (): void => {
    // 节点设置
    TreeNode.config({
        zIndex: 2,
        width: 110,
        height: 43,
        markup: [
            {
                tagName: 'rect',
                selector: 'rect-box'
            },
            {
                tagName: 'text',
                selector: 'text-box'
            },
            {
                tagName: 'g',
                selector: 'collapseBtnGroup',
                children: [
                    {
                        tagName: 'rect',
                        selector: 'collapseBtn',
                        attrs: {
                            'pointer-events': 'visiblePainted'
                        }
                    },
                    {
                        tagName: 'path',
                        selector: 'collapseBtnSymbol',
                        attrs: {
                            fill: 'none',
                            'pointer-events': 'none'
                        }
                    }
                ]
            },
            {
                tagName: 'g',
                selector: 'delBtnGroup',
                children: [
                    {
                        tagName: 'rect',
                        selector: 'delBtn',
                        attrs: {
                            'pointer-events': 'visiblePainted'
                        }
                    },
                    {
                        tagName: 'path',
                        selector: 'delBtnSymbol',
                        attrs: {
                            'pointer-events': 'none'
                        }
                    }
                ]
            }
        ],
        attrs: {
            'rect-box': {
                rx: 4,
                ry: 4,
                refWidth: '100%',
                refHeight: '100%',
                fill: '#1890ff',
                cursor: 'default'
            },
            'text-box': {
                x: 55,
                y: 26,
                fontSize: 14,
                fill: '#fff',
                textAnchor: 'middle',
                cursor: 'pointer',
                event: 'node:viewItem',
                textWrap: {
                    width: -20, // 宽度减少 20px
                    height: '50%', // 高度为参照元素高度的一半
                    ellipsis: true, // 文本超出显示范围时，自动添加省略号
                    breakWord: true // 是否截断单词
                }
            },
            collapseBtnGroup: {
                display: 'none', // 默认不显示收/展按钮
                refX: '100%',
                refY: '50%'
            },
            collapseBtn: {
                fill: '#5F95FF',
                stroke: 'none',
                x: -10,
                y: -10,
                height: 20,
                width: 20,
                rx: 10,
                ry: 10,
                cursor: 'pointer',
                event: 'node:collapse'
            },
            collapseBtnSymbol: {
                refX: -4.5,
                refY: -5,
                stroke: '#FFFFFF'
            },
            delBtnGroup: {
                refX: 100,
                refY: -10,
                display: 'none'
            },
            delBtn: {
                fill: '#5F95FF',
                cursor: 'pointer',
                height: 20,
                width: 20,
                rx: 10,
                ry: 10,
                event: 'node:delItem'
            },
            delBtnSymbol: {
                d: 'M 0 0 L 8 8 M 8 0 L 0 8',
                stroke: '#FFFFFF',
                refX: 6,
                refY: 6,
                'stroke-width': 1.5
            }
        }
    });
    // 边设置
    TreeEdge.config({
        zIndex: 1,
        attrs: {
            line: {
                strokeWidth: 2,
                stroke: '#A2B1C3',
                sourceMarker: null,
                targetMarker: null
            }
        }
    });
    // 注册
    Node.registry.register('tree-node', TreeNode, true);
    Edge.registry.register('tree-edge', TreeEdge, true);
};
// 初始化-画布
const initGraph = (): void => {
    // 创建图形实例
    graph.value = new Graph({
        container: containerBoxRef.value,
        autoResize: true,
        panning: true,
        mousewheel: true,
        interacting: false,
        grid: {
            visible: true
        }
    });
    // 节点事件绑定
    graph.value.on('node:collapse', ({ node }: { node: TreeNode }) => {
        node.toggleCollapse();
        const collapsed = node.isCollapsed();
        const run = (pre: TreeNode) => {
            const succ = graph.value?.getSuccessors(pre, { distance: 1 });
            if (succ) {
                succ.forEach((node: any) => {
                    node.toggleVisible(!collapsed);
                    if (!node.isCollapsed()) {
                        run(node);
                    }
                });
            }
        };
        run(node);
    });
    graph.value.on('node:mouseenter', ({ node }: { node: TreeNode }) => {
        node.operBtnVisibility(true);
    });
    graph.value.on('node:mouseleave', ({ node }: { node: TreeNode }) => {
        node.operBtnVisibility(false);
    });
    // 业务事件
    graph.value.on('node:viewItem', ({ node }: { node: TreeNode }) => {
        console.log('node:viewItem', node.data);
    });
    graph.value.on('node:delItem', ({ node }: { node: TreeNode }) => {
        console.log('node:delItem', node.data);
    });
};
// 初始化
const init = () => {
    initGraph();
    initNodeUnit();
    getNodeData();
    getDiscreteData();
};

// 组件挂载时初始化
onMounted(() => {
    init();
});
// 组件销毁时销毁画布实例
onUnmounted(() => {
    graph.value?.dispose();
});
</script>
<style scoped lang="scss">
.topology-wrapper {
    height: calc(100vh - 341px);
    .topology-box {
        display: flex;
        width: 100%;
        height: calc(100% - 24px);
        .toolbar-box {
            width: 150px;
            padding-top: 20px;
            padding-right: 20px;
            box-sizing: border-box;
            display: flex;
            flex-direction: column;
            ul {
                margin-top: 20px;
                flex: 1;
                overflow: auto;
                padding: 0;
                .tool-item {
                    width: 110px;
                    padding: 10px 15px;
                    box-sizing: border-box;
                    overflow: hidden; /*把超出的内容进行隐藏*/
                    white-space: nowrap; /*设置内容不换行*/
                    text-overflow: ellipsis;
                    margin: 0 auto;
                    margin-bottom: 10px;
                    background-color: #1890ff;
                    color: white;
                    border-radius: 4px;
                    cursor: grab;
                    text-align: center;
                    transition: all 0.3s ease;
                    user-select: none;
                    &:hover {
                        background-color: #40a9ff;
                        transform: translateX(5px);
                    }
                    &:active {
                        cursor: grabbing;
                    }
                }
            }
        }
        .container-box {
            flex: 1;
            overflow: auto;
        }
    }
}
</style>
